/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       steal_enchantment.h

	$Header: /game/steal_enchantment.h $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "steal_enchantment.h"

#include "battlefield.h"
#include "can_cast.h"
#include "combat_creature.h"
#include "combat_creature_ptr.h"
#include "combat_spell_registration.h"
#include "external_string.h"
#include "mass_spell.h"
#include "missile_type.h"
#include "random.h"
#include "replace_keywords.h"
#include "spell_properties.h"

// ----------------------------------------------------------
// steal an enchantment
// ---------------------------------------------------------
namespace
{
	t_external_string const k_combat_action_steal_enchantment( "combat_action.steal_enchantment" );

	class t_receive_spell : public t_handler_base_2<t_combat_creature_ptr, t_map_point_2d>
	{
	public:
		t_receive_spell( t_combat_creature_ptr target, t_spell spell, t_combat_action_message message );

		virtual void operator()( t_combat_creature_ptr victim, t_map_point_2d );
	protected:
		t_combat_action_message m_message;
		t_spell				    m_spell;
		t_combat_creature_ptr   m_target;
	};
};

static t_combat_spell_registration<t_steal_enchantment>
       k_steal_enchantment_registration( k_spell_steal_enchantment );

// ----------------------------------------------------------
// steal an enchantment
// ---------------------------------------------------------
t_receive_spell::t_receive_spell( t_combat_creature_ptr target, t_spell spell, t_combat_action_message message  )
{
	m_target = target;
	m_spell = spell;
	m_message = message;
}

// ----------------------------------------------------------
// steal an enchantment
// ---------------------------------------------------------
void t_receive_spell::operator()( t_combat_creature_ptr victim, t_map_point_2d )
{
	t_battlefield& battlefield = m_target->get_battlefield();

	battlefield.start_animation( *m_target, m_spell, m_message );
}

// ----------------------------------------------------------
// steal an enchantment
// ---------------------------------------------------------
t_steal_enchantment::t_steal_enchantment( t_battlefield& battlefield, t_spell spell )
                   : t_combat_spell_single_target( battlefield, spell )
{
}

// ----------------------------------------------------------
// steal an enchantment
// ---------------------------------------------------------
double t_steal_enchantment::ai_weight( t_combat_creature const& target) const
{
	// Not implemented
	return 0.0;
}

// ----------------------------------------------------------
// steal an enchantment
// ---------------------------------------------------------
bool t_steal_enchantment::steal( t_combat_creature_ptr caster, 
								 t_combat_creature_ptr target,
								 bool build_special_message,
								 t_combat_action_message & message )
{
	t_spell_list const&			 spells = target->get_active_spells();
	t_spell_list			     possible_spells;
	t_spell_list::const_iterator spell = spells.begin();
	t_spell_list::const_iterator spells_end = spells.end();
	int							 choice;

	for (; spell != spells.end(); spell++)
	{
		if (check_steal_enchantment( *caster, *spell ))
			possible_spells.push_back( *spell );
	}
	if (possible_spells.empty())
		return false;

	choice = random( possible_spells.size() );
	for (spell = possible_spells.begin(); choice-- > 0; spell++)
		;

	t_combat_creature_list           receivers;
	t_combat_creature_list::iterator index = m_battlefield.creatures_begin();
	t_combat_creature_list::iterator end = m_battlefield.creatures_end();

	for (; index != end; index++)
	{
		if (index->get()->get_number() == 0)
			continue;
		if (index->get()->get_controller() != caster->get_controller())
			continue;
		if (can_affect( *spell, **index ))
			receivers.push_back( *index );
	}
	if (receivers.empty())
		return false;

	choice = random( receivers.size() );
	for (index = receivers.begin(); choice-- > 0; index++)
		;
	
	t_missile_handler handler;

	if ( build_special_message )
	{
		// Build the special "Steal <name>" message, and pass it out
		message.set_creature( *caster );
		message.set_text( replace_keywords( k_combat_action_steal_enchantment, "%spell", get_short_spell_name( *spell ) ) );
		message.set_priority( k_combat_action_message_priority_steal_enchantment );
	}

	index->get()->set_spell( *spell, message, target->get_effect_power( *spell ));
	if (*spell == k_spell_martyr)
		index->get()->set_martyr_protector( target->get_martyr_protector() );
	handler = new t_receive_spell( *index, *spell, message );
	target->clear_spell( *spell, message );
	m_battlefield.launch_missile( k_missile_mana, *target, 
		                           target->get_body_center(), 
								   index->get()->get_body_center(),
								   handler, message );
	return true;
}

// ----------------------------------------------------------
// steal an enchantment
// ---------------------------------------------------------
bool t_steal_enchantment::cast_on( t_combat_creature_ptr caster, 
		                           t_combat_creature_ptr target, 
								   int power,
								   bool is_mirror )
{
	t_combat_action_message message;

	// Steal will fill in message
	if (!steal( caster, target, true, message ))
		return false;

	// Use message passed out from steal
	m_battlefield.start_animation( *target, k_spell_steal_enchantment, message );
	return true;
}

double t_steal_enchantment::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}

// ----------------------------------------------------------
// steal all enchantments
// ---------------------------------------------------------
namespace
{
	class t_steal_all : public t_mass_spell_base
	{
	public:
		t_steal_all( t_battlefield& battlefield, t_spell spell );

		virtual void            execute( t_combat_creature& caster );
		virtual double			get_cancel_weight( t_combat_creature const& target ) const;
	};
};

static t_combat_spell_registration<t_steal_all> const
       k_steal_all_registration( k_spell_steal_all_enchantments );

// ----------------------------------------------------------
// steal all enchantments
// ---------------------------------------------------------
t_steal_all::t_steal_all( t_battlefield& battlefield, t_spell spell )
           : t_mass_spell_base( battlefield, spell )
{
}

// ----------------------------------------------------------
// steal all enchantments
// ---------------------------------------------------------
void t_steal_all::execute( t_combat_creature& )
{
	t_counted_ptr<t_steal_enchantment> stealer;
	t_combat_creature_list             subjects;
	t_combat_action_message			   message( *m_caster, get_action_text( false ) );

	stealer = new t_steal_enchantment( m_battlefield, k_spell_steal_enchantment );
	stealer->set_cast_spell( m_spell );

	pay_cost();

	while (true)
	{
		t_combat_creature_list::iterator index = m_battlefield.creatures_begin();
		t_combat_creature_list::iterator end = m_battlefield.creatures_end();

		for (; index != end; index++)
		{
			if (index->get()->get_number() == 0)
				continue;
			if (index->get()->get_controller() == m_caster->get_controller())
				continue;
			if (can_cast_on( index->get(), 0 ))
				break;
		}
		if (index == end)
			return;
		if (std::find( subjects.begin(), subjects.end(), *index ) == subjects.end())
		{
			m_battlefield.start_animation( **index, k_spell_steal_enchantment, message );
			subjects.push_back( *index );
		}
		stealer->steal( m_caster, *index, false, message );
	}
}

double	t_steal_all::get_cancel_weight( t_combat_creature const& target ) const
{
	return 0.0;
}
